/*
 * Copyright (c) 2007-2008, Thomas Krause (tho.krause@gmail.com)
 * 
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 
 *     * Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *     * Neither the name of gidoo nor the names of its contributors may be used to
 *       endorse or promote products derived from this software without specific
 *       prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.gidoo.cms.login;

import javax.jcr.LoginException;
import org.gidoo.cms.helper.Crypto;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.query.QueryManager;
import org.apache.wicket.Request;
import org.apache.wicket.authentication.AuthenticatedWebSession;
import org.apache.wicket.authorization.strategies.role.Roles;
import org.gidoo.cms.GidooCMS;

/**
 * 
 * @author thomas
 */
public class GidooCMSSession extends AuthenticatedWebSession
{
  
  public static final String ROLE_PUBLISHER = "PUBLISHER";
  public static final String ROLE_EDITOR = "EDITOR";
  
  public static final String ANONYMOUS = "anonymous";
  public static final SimpleCredentials ANONYMOUS_CREDENTIALS =
    new SimpleCredentials(ANONYMOUS, new char[0]);
   
  private String lastVisitedFolder;
  
  private String username;
  
  transient private Session jcrSession;
  
  public GidooCMSSession(Request req)
  {
    super(req);
  }
  
  
  public boolean authenticate(final String username, final String password)
  {
    boolean result = false;
    try
    {
      String usernamedFixed = username.replaceAll("/", "");
      GidooCMS app = (GidooCMS) getApplication();
      
      Session tmpSession = app.getRepository().login(GidooCMSSession.ANONYMOUS_CREDENTIALS);
      Node usersNode = 
        tmpSession.getRootNode().getNode("users");
      
      if(usersNode.hasNode(usernamedFixed))
      {
        String md5 = Crypto.calculateSHAHash(password);
      
        Node tempUserNode = usersNode.getNode(usernamedFixed);
        
        result = md5.equals(tempUserNode.getProperty("passwordhash").getString());

        if(result)
        {
          if(jcrSession != null)
          {
            jcrSession.logout();
          }
          
          this.username = usernamedFixed;
          
          jcrSession = app.getRepository().login(
            new SimpleCredentials(usernamedFixed, password.toCharArray()));
        }
      }
      
      tmpSession.logout();
    }
    catch(Exception ex)
    {
      jcrSession = null;
      result = false;
      Logger.getLogger(GidooCMSSession.class.getName()).log(Level.SEVERE, null, ex);
    }
    return result;
  }

  @Override
  public void signOut()
  {
    super.signOut();
    
    jcrSession.logout();
  }

  @Override
  protected void finalize() throws Throwable
  {
    super.finalize();
    
    if(jcrSession != null)
    {
      jcrSession.logout();
    }
    
  }

  
  
  /** 
   * Return the JCR session associated with this session.
   * 
   * @return The session or null if not logged in;
   */
  public Session getJcrSession()
  {
    if(jcrSession == null || !jcrSession.isLive())
    {
      try
      {

        GidooCMS app = (GidooCMS) getApplication();
        if(app != null && app.getRepository() != null)
        {
          jcrSession = app.getRepository().login(GidooCMSSession.ANONYMOUS_CREDENTIALS);
          
          if(isFirstRuntime())
          {
            jcrSession.logout();
            
            jcrSession = app.getRepository().login(
              new SimpleCredentials( "firstUser", new char[0]));
          }
        }
      }
      catch (LoginException ex)
      {
        Logger.getLogger(GidooCMSSession.class.getName()).log(Level.SEVERE, null, ex);
      }
      catch (RepositoryException ex)
      {
        Logger.getLogger(GidooCMSSession.class.getName()).log(Level.SEVERE, null, ex);
      }
    }
      
    return jcrSession;
  }


  /** 
   * Return the root node of the JCR session.
   * 
   * @return The root node or null if not logged in.
   */
  public Node getJcrRootNode() throws RepositoryException
  {
    if(getJcrSession() != null)
    {
      return getJcrSession().getRootNode();
    }
    return null;
  }
  
  public Roles getRoles()
  {
    // fallback solution if no user is created yet
    if(isFirstRuntime())
    {
      Roles r = new Roles();
      r.add(Roles.ADMIN);
      return r;
    }
  
    if(isSignedIn())
    {
      Roles r = new Roles();
      
      try
      {
        Node userInfo = getJcrRootNode().getNode("users").getNode(getUsername());
          
        try
        {
          
          Node publishNode = userInfo.getNode("publish");

          if(publishNode != null && publishNode.getNodes().hasNext())
          {
            r.add(ROLE_PUBLISHER);
          }
        }
        catch(PathNotFoundException ex)
        {
        }

        try
        {
          Node editNode = userInfo.getNode("edit");

          if(editNode != null && editNode.getNodes().hasNext())
          {
            r.add(ROLE_EDITOR);
          }
        }
        catch(PathNotFoundException ex)
        {
        }
        if(userInfo.getProperty("isadmin").getBoolean())
        {
          r.add(Roles.ADMIN);
        }
        
        return r;
      }
      catch(RepositoryException ex)
      {
        Logger.getLogger(GidooCMSSession.class.getName()).log(Level.SEVERE, null, ex);
      }
    }
    return null;
  }

  /** Checks wether this CMS is the first time in use (there are no users or pages) */
  private boolean isFirstRuntime()
  {
   try
    {
      Node rNode = getJcrRootNode();
      if(rNode != null)
      {
        Node users = rNode.getNode("users");
        NodeIterator itUsers = users.getNodes();
        
        Node sections = rNode.getNode("sections");
        NodeIterator itSections = sections.getNodes();
        if(!itUsers.hasNext() && !itSections.hasNext())
        {
          return true;
        }
      }
    }
    catch(RepositoryException ex)
    {
      Logger.getAnonymousLogger().log(Level.SEVERE, null, ex);
    }
    return false;
  }

  /** Returns the username of the current logged in user or null */
  public String getUsername()
  {
    return username;
  }
  

  /** Get the last visited folder of this session. */
  public Node getLastVisitedFolder()
  {
    Node result = null;
    try
    {
      result = getJcrRootNode().getNode(lastVisitedFolder.substring(1));
    }
    catch(Exception ex)
    {
      result = null;
    }
      
    return result;
  }

    
  /** Set the last visited folder of this session. */
  public void setLastVisitedFolder(Node lastVisitedFolder)
  {
    try
    {
      this.lastVisitedFolder = lastVisitedFolder.getPath();
    }
    catch(RepositoryException ex)
    {
      this.lastVisitedFolder = null;
    }
  }
  
  /** Get the query manager */
  public QueryManager getQueryManager() throws RepositoryException
  {
    return getJcrSession().getWorkspace().getQueryManager();
  }

  
  
}
